import logging
import re
import time

import requests
from bs4 import BeautifulSoup
from tenacity import retry, stop_after_attempt
from lxml import etree

from exception import *
from predict import predict

base_url = "http://119.145.67.59"
login_url = base_url + "/default2.aspx"
img_url = base_url + "/CheckCode.aspx"
lesson_url = base_url + "/xf_xsqxxxk.aspx"


class Spider:
    def __init__(self, uid: str, pwd: str, search_lesson: str) -> None:
        self.success = False
        self.uid = uid
        self.pwd = pwd
        self.search_lesson_name = search_lesson
        self.target_lesson_name = ""

        self.session = requests.session()
        self.session.headers = {
            'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5'
        }
        self.__VIEWSTATE = ""

    @retry(stop=stop_after_attempt(8))
    def login(self):
        # 首次打开登录界面 目的是获取Cookie: ASP.NET_SessionId=5km0xo554fkxrwix01ecmim2 和 viewstate
        response = self.session.get(login_url, timeout=5)
        # 获取viewstate
        selector = etree.HTML(response.content)
        __VIEWSTATE = selector.xpath('//*[@id="form1"]/input/@value')[0]
        img_response = self.session.get(img_url, stream=True, timeout=5)
        img_stream = img_response.content
        verify_code = predict(img_stream)
        login_data = {
            "__VIEWSTATE": __VIEWSTATE,
            "txtUserName": self.uid,
            "TextBox2": self.pwd,
            "txtSecretCode": verify_code,
            "RadioButtonList1": "学生".encode('gb2312'),
            "Button1": "",
            "lbLanguage": "",
            "hidPdrs": "",
            "hidsc": ""
        }

        login_response = self.session.post(login_url, data=login_data, timeout=5)

        # 登录成功跳转302
        if login_response.status_code != requests.codes.found:
            # 登录失败
            # 查找警告
            pattern = r"alert\('(\w*)"
            alert = re.search(pattern, login_response.text)
            if alert:
                alert_info = alert.group(1)
                raise LoginException(alert_info)
            raise ResponseException()

        return self

    @retry(stop=stop_after_attempt(8))
    def search_lesson(self):
        # 打开选课界面
        lesson_response = self.session.get(lesson_url, timeout=5)
        if lesson_response.status_code != requests.codes.ok:
            raise ResponseException()
        # 获取viewstate
        selector = etree.HTML(lesson_response.content)
        __VIEWSTATE = selector.xpath('//*[@id="form1"]/input/@value')[0]

        # 查询课程
        query_data = {
            "__EVENTTARGET": "",
            "__EVENTARGUMENT": "",
            "__VIEWSTATE": __VIEWSTATE,
            "ddl_kcxz": "",
            "ddl_ywyl": "有".encode('gb2312'),  # 有无余量
            "ddl_kcgs": "",
            "ddl_xqbs": 1,
            "ddl_sksj": "",
            "TextBox1": self.search_lesson_name.encode('gb2312'),  # 查询课程名
            "Button2": "确定".encode("gb2312"),
            "txtPageSize": 15
        }

        search_response = self.session.post(lesson_url, data=query_data, timeout=5)
        if search_response.status_code != requests.codes.ok:
            raise ResponseException()
        # 获取viewstate
        soup = BeautifulSoup(search_response.text, "lxml")
        self.__VIEWSTATE = soup.find('input', attrs={'name': '__VIEWSTATE'})['value']

        # 解析课程列表
        soup.find('table', id='kcmcGrid')
        lessons_tag = soup.find('table', id='kcmcGrid')
        lesson_tag_list = lessons_tag.find_all('tr')[1:]
        remaining_lesson_num = len(lesson_tag_list)
        if remaining_lesson_num == 0:
            raise Exception("无剩余课程")

        # 列表的第一个课程是选课目标
        first_lesson_tag = lesson_tag_list[0]
        td_list = first_lesson_tag.find_all('td')
        self.target_lesson_name = td_list[2].string
        return self

    @retry(stop=stop_after_attempt(8))
    def grab_lesson(self):
        grab_data = {
            "__EVENTTARGET": "",
            "__EVENTARGUMENT": "",
            "__VIEWSTATE": self.__VIEWSTATE,
            "ddl_kcxz": "",
            "ddl_ywyl": "有".encode('gb2312'),  # 有无余量
            "ddl_kcgs": "",
            "ddl_xqbs": 1,
            "ddl_sksj": "",
            "TextBox1": self.search_lesson_name.encode('gb2312'),  # 查询课程名
            "kcmcGrid:_ctl2:xk": "on",  # 第一个课程选中状态
            "Button1": "  提交  ".encode("gb2312"),
            "txtPageSize": 15
        }
        grab_response = self.session.post(lesson_url, data=grab_data, timeout=5)
        # 查找警告
        pattern = r"alert\('(\w*)"
        alert = re.search(pattern, grab_response.text)
        if alert:
            alert_info = alert.group(1)
            # 选课失败
            raise GrabLessonException(alert_info)
        return self

    def start(self, callback):
        start_time = time.time()
        logging.warning("%s正在抢课", self.uid)
        exp = None
        try:
            self.login().search_lesson().grab_lesson()
        except Exception as e:
            exp = e
        end_time = time.time()
        length = (end_time - start_time) * 1000
        if exp:
            logging.warning("%s抢课失败:%s,耗时%dms", self.uid, exp, length)
        callback(self, exp)
